#try:
    #import stacklesssocket
    #stacklesssocket.install()
    #stacklesssocket.ManageSockets()
#except:
    #pass

import socket
import struct
import pickle
import util
import sys
import time
from service import Service

class NetworkNode(Service):
    MTU_SIZE = 10000
    
    def __init__(self, ip = None, port = None):
        Service.__init__(self)

        self.buffer = []
        self.waiting = False
        self.transfers = []
        self.frameTimes = []
        
        self.ip = ip or socket.gethostname()
        self.port = port or 1234
        
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.sock.setblocking(False)
        
        while True:
            try:
                self.sock.bind((self.ip,self.port))
                break
            except socket.error:
                self.port += 1
                
        self.packetBuffers = {}
        self.receiveFunctions = util.getFunctions(self, "receivePacket_")
                
        self.log("bound to", self.ip, self.port)

    def listenWait(self, cond = None, proc = None, retry = None, retryTime = 1):
        self.waiting = True
        Service.wait(cond, proc, retry, retryTime)
        self.waiting = False
        for cmd, msg, sender in self.buffer:
            #self.log("unbuffer",cmd,msg,sender)
            self.receive(cmd,msg,sender)
        self.buffer = []
        
    def run(self):
        self.wait(None,self.pump)
        #self.wait(None,None,self.pump,0.01)
        
    def kill(self):
        self.sock.close()
        
    def pump(self, forceReceive = None):
        self.frameTimes.append(time.time())
        while True:
            try:
                packet, sender = self.sock.recvfrom(self.MTU_SIZE)
            except socket.error:
                currentTime = time.time()
                while self.transfers and currentTime - self.transfers[0][0] > 1:
                    self.transfers.pop(0)
                while self.frameTimes and currentTime - self.frameTimes[0] > 1:
                    self.frameTimes.pop(0)
                if self.transfers and self.frameTimes:
                    bytes = sum([transfer[1] for transfer in self.transfers])
                    seconds = currentTime - self.transfers[0][0]
                    pumps = len(self.transfers)
                    frames = len(self.frameTimes)
                    if bytes and seconds > 0.01:
                        pass
                        #self.log(seconds,bytes,pumps,frames,float(pumps)/float(frames))
                        #self.log("%f bytes/sec %f pumps/sec %f bytes/pump" % \
                            #(float(bytes)/seconds,float(pumps)/seconds,float(bytes)/float(pumps)))
                        #self.log(self.transfers)
                return
            self.transfers.append((time.time(),len(packet)))
            self.packetBuffers.setdefault(sender,"")
            self.packetBuffers[sender] += packet
            if len(self.packetBuffers[sender]) >= 4:
                packetLen = struct.unpack("i",self.packetBuffers[sender][:4])[0]
                if len(self.packetBuffers[sender]) >= packetLen + 4:
                    packetData = self.packetBuffers[sender][4:packetLen+4]
                    self.packetBuffers[sender] = self.packetBuffers[sender][packetLen+4:]
                    try:
                        cmd, msg = pickle.loads(packetData)
                    except:
                        self.packetBuffers[sender] = ""
                        return
                    if self.waiting and (not forceReceive or cmd not in forceReceive):
                        #self.log("buffer",cmd,msg,sender)
                        self.buffer.append((cmd,msg,sender))
                    else:
                        self.receive(cmd,msg,sender)
        
    def receive(self, cmd, msg, sender):
        #self.log("receive",cmd,msg,sender)
        if cmd in self.receiveFunctions:
            self.receiveFunctions[cmd](*[msg,sender])
            
    def send(self, cmd, msg, dest):
        data = pickle.dumps((cmd,msg))
        dataLen = len(data)
        #self.log("send",cmd,msg,dest,dataLen)
        try:
            if dataLen > self.MTU_SIZE - 4:
                self.log("unpack")
                self.sock.sendto(struct.pack("i",dataLen), dest)
                for start in range(0,dataLen,self.MTU_SIZE):
                    part = data[start:min(start + self.MTU_SIZE, dataLen)]
                    self.sock.sendto(part,dest)
            else:
                self.sock.sendto(struct.pack("i",dataLen) + data, dest)
        except socket.error:
            pass
